

import Foundation
import HandyJSON
import Moya
import LXExtensions

/// 请求json转模型的协议,方便自由选择json库,使用时让基类遵循此协议
public protocol LXJsonMapper: Any {
    static func map(from jsonObject: Any) throws -> Self
    
//    static func map(from jsonArray: [Any]) -> [Self]
    
}

// 数组转换默认使用类转换方法
public extension LXJsonMapper {
    static func map(from jsonArray: [Any]) throws -> [Self] {
        var arr: [Self] = []
        do {
            for item in jsonArray {
                let model = try self.map(from: item)
                arr.append(model)
            }
        } catch let err {
            throw err
        }
        return arr
    }
}


//MARK:- Decodable默认实现

public extension LXJsonMapper where Self: Decodable {
    static func map(from jsonObject: Any) throws -> Self {
        if !JSONSerialization.isValidJSONObject(jsonObject) {
            if let jsonObject = jsonObject as? Self {
                return jsonObject
            }else{
                throw LXError.jsonMapping(nil)
            }
            
        }
        do {
            let data = try JSONSerialization.data(withJSONObject: jsonObject, options: .fragmentsAllowed)
            let decoder = JSONDecoder()
            let model = try decoder.decode(Self.self, from: data)
            return model
        } catch let err {
            throw LXError.objectMapping(err, nil)
        }

    }
}

//MARK:- HandyJSON默认实现

public extension LXJsonMapper where Self: HandyJSON {
    static func map(from jsonObject: Any) throws -> Self {
        if let dic = jsonObject as? [String: Any], let item = Self.deserialize(from: dic) {
            return item
        }else if let str = jsonObject as? String, let item = Self.deserialize(from: str) {
            return item
        }
        throw LXError.jsonMapping(nil)
    }
    
    static func map(from jsonArray: [Any]) throws -> [Self] {
        guard let arr = [Self].deserialize(from: jsonArray) else {
            throw LXError.jsonMapping(nil)
        }
        guard let nextArr: [Self] = arr.filter({ item in
            return item != nil
        }) as? [Self]  else {
            throw LXError.jsonMapping(nil)
        }
        return nextArr
    }
}


public extension Moya.Response {
    func mapObject<L: LXJsonMapper>(_ type: L.Type, atKeyPath keyPath: String? = nil) throws -> L {
        var jsonData: Any = try mapJSON(failsOnEmptyData: true)
        if let keyPath = keyPath {
            if let jsonObject = jsonData as? [String: Any], let item = jsonObject[keyPath]  {
                jsonData = item
            }else {
                throw MoyaError.jsonMapping(self)
            }
        }
        
        let model: L = try L.map(from: jsonData)
        return model
    }
    
    func mapArray<L: LXJsonMapper>(_ type: L.Type, atKeyPath keyPath: String? = nil) throws -> [L] {
        var jsonData: Any = try mapJSON(failsOnEmptyData: true)
        if let keyPath = keyPath {
            if let jsonObject = jsonData as? [String: Any], let item = jsonObject[keyPath]  {
                jsonData = item
            }else {
                throw MoyaError.jsonMapping(self)
            }
        }
        if !JSONSerialization.isValidJSONObject(jsonData) {
            throw MoyaError.jsonMapping(self)
        }
        guard let jsonArray = jsonData as? [Any] else {
            throw MoyaError.jsonMapping(self)
        }
        let model: [L] = try L.map(from: jsonArray)
        return model
    }

}
//MARK:- 让基础类型也支持LXJsonMapper协议,并支持简单的值转换,这样方便转换成基础类型
//因为这些基础类型已经支持了Decodable协议,需要注意覆盖问题

extension String: LXJsonMapper {
    public static func map(from jsonObject: Any) throws -> String {
        if jsonObject is Int
            || jsonObject is Float
            || jsonObject is Double
            || jsonObject is CGFloat{
            return "\(jsonObject)"
        }
        guard let str = jsonObject as? String else {
            throw LXError.jsonMapping(nil)
        }
        return str
    }
}

extension Int: LXJsonMapper {
    public static func map(from jsonObject: Any) throws -> Int {
        if jsonObject is String{
            guard let item = Int(jsonObject as! String) else {
                throw LXError.jsonMapping(nil)
            }
            return item
        }
        if jsonObject is Float{
            return Int(jsonObject as! Float)
        }
        if jsonObject is Double{
            return Int(jsonObject as! Double)
        }
        if jsonObject is CGFloat{
            return Int(jsonObject as! CGFloat)
        }
        guard let item = jsonObject as? Int else {
            throw LXError.jsonMapping(nil)
        }
        return item
    }
}

extension Float: LXJsonMapper {
    public static func map(from jsonObject: Any) throws -> Float {
        if jsonObject is String{
            guard let item = Float(jsonObject as! String) else {
                throw LXError.jsonMapping(nil)
            }
            return item
        }
        if jsonObject is Int{
            return Float(jsonObject as! Int)
        }
        if jsonObject is Double{
            return Float(jsonObject as! Double)
        }
        if jsonObject is CGFloat{
            return Float(jsonObject as! CGFloat)
        }
        guard let item = jsonObject as? Float else {
            throw LXError.jsonMapping(nil)
        }
        return item
    }
}

extension Double: LXJsonMapper {
    public static func map(from jsonObject: Any) throws -> Double {
        if jsonObject is String{
            guard let item = Double(jsonObject as! String) else {
                throw LXError.jsonMapping(nil)
            }
            return item
        }
        if jsonObject is Int{
            return Double(jsonObject as! Int)
        }
        if jsonObject is Float{
            return Double(jsonObject as! Float)
        }
        if jsonObject is CGFloat{
            return Double(jsonObject as! CGFloat)
        }
        guard let item = jsonObject as? Double else {
            throw LXError.jsonMapping(nil)
        }
        return item
    }
}

extension CGFloat: LXJsonMapper {
    public static func map(from jsonObject: Any) throws -> CGFloat {
        if jsonObject is String{
            guard let item = Float(jsonObject as! String) else {
                throw LXError.jsonMapping(nil)
            }
            return CGFloat(item)
        }
        if jsonObject is Int{
            return CGFloat(jsonObject as! Int)
        }
        if jsonObject is Float{
            return CGFloat(jsonObject as! Float)
        }
        if jsonObject is Double{
            return CGFloat(jsonObject as! Double)
        }
        guard let item = jsonObject as? CGFloat else {
            throw LXError.jsonMapping(nil)
        }
        return item
    }
}

extension Dictionary: LXJsonMapper {
    public static func map(from jsonObject: Any) throws -> Dictionary {
        if jsonObject is String{
            let str = jsonObject as! String
            guard let dic = str.lx_toJsonObject() as? Dictionary else {
                throw LXError.jsonMapping(nil)
            }
            return dic
            
        }
        
        guard let dic = jsonObject as? Dictionary else {
            throw LXError.jsonMapping(nil)
        }
        return dic
    }
}


